package other.getech.oa.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.rkhd.platform.sdk.data.model.User;
import com.rkhd.platform.sdk.exception.ApiEntityServiceException;
import com.rkhd.platform.sdk.exception.CustomConfigException;
import com.rkhd.platform.sdk.exception.XsyHttpException;
import com.rkhd.platform.sdk.http.*;
import com.rkhd.platform.sdk.log.Logger;
import com.rkhd.platform.sdk.log.LoggerFactory;
import com.rkhd.platform.sdk.service.CustomConfigService;
import com.rkhd.platform.sdk.service.XObjectService;
import org.apache.commons.lang.CharEncoding;
import other.getech.oa.model.*;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 需求:OA待办相关服务类
 */
public class OAService {
    private static final Logger logger = LoggerFactory.getLogger();
    private static final String username;//认证用户名
    private static final String password;//认证密码
    private static final String oaBaseUrl;//OA待办地址前缀
    private static final String sendTodoUrl;//发送待办接口
    private static final String deleteTodoUrl;//删除待办接口
    private static final String setTodoDoneUrl;//设为已办接口
    private static final String getTodoUrl;//获取待办接口

    private final String appName = "getech_neocrm";//待办来源 自己定义的标识，用于将来查询oa所有待办的过滤条件
    private final String baseUrl = "https://login.tcl.com/siam/oauth2.0/authorize?client_id=getech_crm&response_type=code&redirect_uri=https://crm-p05.xiaoshouyi.com/global/sso/callback/00AP44854013D48572D42963557E5B002D08.action%3Freturn_uri";
    private final String crmUrl = "/bff/neoweb#/entityDetail/%s/%s?objectApiKey=%s&recordId=%s&objectId=%s"; //PC详情页面 第一、三个%s为entityApiKey,第二、四个%s为数据ID,第五个%s为数据对象objectId
    // /bff/neoh5#/neo/entityDetail/${objectApiKey}/${recordId}?platform=mobile&objectApiKey=${objectApiKey}&recordId=${recordId}
    private final String crmH5Url = "/bff/neoh5#/neo/entityDetail/%s/%s?platform=mobile&objectApiKey=%s&recordId=%s"; //H5详情页面 第一、三个%s为entityApiKey,第二、四个%s为数据ID

    static {
        try {
            //加载自定义配置中的账号信息
            Map<String, String> configSet = CustomConfigService.instance().getConfigSet("oaConfig");
            if (configSet != null) {
                username = configSet.get("username");
                password = configSet.get("password");
                oaBaseUrl = configSet.get("oaBaseUrl");
            } else {
                username = "testtodo"; //认证用户名
                password = "testtodo123"; //认证密码
                oaBaseUrl = "https://ep-uat.tcl.com";
            }
            sendTodoUrl = oaBaseUrl + "/api/sys-notify/sysNotifyTodoRestService/sendTodo"; //发送待办接口
            deleteTodoUrl = oaBaseUrl + "/api/sys-notify/sysNotifyTodoRestService/deleteTodo"; //删除待办接口
            setTodoDoneUrl = oaBaseUrl + "/api/sys-notify/sysNotifyTodoRestService/setTodoDone"; //设为已办接口
            getTodoUrl = oaBaseUrl + "/api/sys-notify/sysNotifyTodoRestService/getTodo"; //获取待办接口
        } catch (CustomConfigException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 提交后
     *
     * @param entityApiKey  对象api名称
     * @param dataId        数据ID
     * @param usertaskLogId 当前任务ID
     */
    public void submit(String entityApiKey, Long dataId, Long usertaskLogId) throws XsyHttpException, ApiEntityServiceException {
        sendTodo(entityApiKey, dataId, usertaskLogId);
    }

    /**
     * 节点同意后
     *
     * @param entityApiKey  对象api名称
     * @param dataId        数据ID
     * @param usertaskLogId 当前任务ID
     */
    public void agree(String entityApiKey, Long dataId, Long usertaskLogId) throws XsyHttpException, ApiEntityServiceException {
        handleTodo(entityApiKey, dataId, usertaskLogId, "agree");
    }

    /**
     * 节点拒绝后
     *
     * @param entityApiKey  对象api名称
     * @param dataId        数据ID
     * @param usertaskLogId 当前任务ID
     */
    public void reject(String entityApiKey, Long dataId, Long usertaskLogId) throws XsyHttpException, ApiEntityServiceException {
        handleTodo(entityApiKey, dataId, usertaskLogId, "reject");
    }

    /**
     * 流程撤回后
     *
     * @param entityApiKey  对象api名称
     * @param dataId        数据ID
     * @param usertaskLogId 当前任务ID
     */
    public void withdraw(String entityApiKey, Long dataId, Long usertaskLogId) throws XsyHttpException, ApiEntityServiceException {
        handleTodo(entityApiKey, dataId, usertaskLogId, "withdraw");
    }

    /**
     * 发送待办接口
     *
     * @param entityApiKey  对象api名称
     * @param dataId        数据ID
     * @param usertaskLogId 当前任务ID
     */
    public void sendTodo(String entityApiKey, Long dataId, Long usertaskLogId) throws XsyHttpException, ApiEntityServiceException {
        List<FlowHistory> flowHistories = creekflowHistory(entityApiKey, dataId, false);
        logger.info(String.format("查询到历史:%s", flowHistories.size()));
        if (flowHistories.size() == 1) {
            logger.info("审批流程直接结束！");
            return;//说明流程直接结束
        }

        Optional<FlowHistory> optionalFlowHistory = flowHistories.stream().filter(f -> Objects.equals(f.getId(), usertaskLogId)).findFirst();
        List<String> loginNames = null;
        Long procInstId = null;
        if (optionalFlowHistory.isPresent()) {
            FlowHistory currentFlow = optionalFlowHistory.get();
            procInstId = currentFlow.getProcInstId();
            logger.info(String.format("currentFlow::%s", JSON.toJSONString(currentFlow)));
            //找出大于当前节点创建时间是下一节点并且状态等于5：待办理
            List<FlowHistory> list = flowHistories.stream().filter(f -> f.getStatus() == 5 && f.getCreatedAt() > currentFlow.getCreatedAt()).collect(Collectors.toList());
            if (list.isEmpty()) {
                logger.info("没有满足OA待办的人结束！");
                return;
            }
            List<Long> userIds = list.stream().map(FlowHistory::getAssigneeId).collect(Collectors.toList());
            if (userIds.isEmpty()) {
                logger.info("没有下个节点的审批人结束！");
                return;
            }

            List<User> users = XObjectService.instance().getByIds("user", userIds, true);
            logger.info(String.format("users:%s", JSON.toJSONString(users)));
            if (!users.isEmpty())
                loginNames = users.stream().filter(u -> u.getYesNo__c() == 1).map(User::getLoginName__c).collect(Collectors.toList());
        }
        if (loginNames == null) {
            logger.info("下个节点的审批人不推送OA待办结束！");
            return;
        }

        //对象api名称+数据ID+流程实例ID 用“-”拼接成一个唯一标识
//        String modelId = String.format("%s-%s-%s", entityApiKey, dataId, procInstId);//OA的modelId字段长度只有50
        //数据ID+流程实例ID 用“-”拼接成一个唯一标识
        String modelId = String.format("%s-%s", dataId, procInstId);

        for (String loginName : loginNames) {
            JSONObject loginNameObj = new JSONObject().fluentPut("LoginName", loginName);

            NotifyTodoSendContext n = new NotifyTodoSendContext();
            n.setAppName(appName);//待办来源
            n.setModelId(modelId);//待办唯一标识
            n.setModelName(entityApiKey);//模块名
            n.setKey(entityApiKey);//关键字
            n.setType(1);//待办类型
            n.setLevel(3);//待办优先级
            n.setCreateTime(milliToLocalDateTimeStr(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"));//创建时间
            n.setTargets(loginNameObj.toJSONString());//待办所属对象
            n.setDocCreator(loginNameObj.toJSONString());//待办创建者

            n.setMobileLink(encodeURL(String.format(crmH5Url, entityApiKey, dataId, entityApiKey, dataId)));//移动端链接
            n.setPadLink(encodeURL(String.format(crmH5Url, entityApiKey, dataId, entityApiKey, dataId)));//pad端链接

            if (Objects.equals(entityApiKey, "quote")) {
                n.setSubject("报价单审批待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 409)));//链接
            }
            if (Objects.equals(entityApiKey, "order")) {
                n.setSubject("订单审批待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 35)));//链接
            }
            if (Objects.equals(entityApiKey, "contract")) {
                n.setSubject("合同审批待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 15)));//链接
            }
            if (Objects.equals(entityApiKey, "ResourceRequest__c")) {
                n.setSubject("号码开通终审待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3215367338265924L)));//链接
            }
            if (Objects.equals(entityApiKey, "bid__c")) {
                n.setSubject("业务机会投标审批待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3220833882115426L)));//链接
            }
            if (Objects.equals(entityApiKey, "debooking__c")) {
                n.setSubject("Debooking审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3226616856825119L)));//链接
            }
            if (Objects.equals(entityApiKey, "customerRelevance__c")) {
                n.setSubject("客户报备审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3330985714768543L)));//链接
            }
            if (Objects.equals(entityApiKey, "account")) {
                n.setSubject("客户审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 1L)));//链接
            }
            if (Objects.equals(entityApiKey, "sporadicProcurement__c")) {
                n.setSubject("零星采购审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3305462143654474L)));//链接
            }
            if (Objects.equals(entityApiKey, "campaign")) {
                n.setSubject("市场活动审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 6L)));//链接
            }
            if (Objects.equals(entityApiKey, "bidSeal__c")) {
                n.setSubject("投标盖章审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3345110021462633L)));//链接
            }
            if (Objects.equals(entityApiKey, "customEntity39__c")) {
                n.setSubject("其他销售类文件审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3345223832787618L)));//链接
            }
            if (Objects.equals(entityApiKey, "invoice__c")) {
                n.setSubject("开票审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3309730160482884L)));//链接
            }
            if (Objects.equals(entityApiKey, "redInvoice__c")) {
                n.setSubject("红冲开票审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3309769249299108L)));//链接
            }
            if (Objects.equals(entityApiKey, "claim__c")) {
                n.setSubject("认领审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3309786730699350L)));//链接
            }
            if (Objects.equals(entityApiKey, "claimRefund__c")) {
                n.setSubject("认领作废审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3373553948170901L)));//链接
            }
            if (Objects.equals(entityApiKey, "performanceSplit__c")) {
                n.setSubject("业绩拆分审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3479989015206496L)));//链接
            }
            if (Objects.equals(entityApiKey, "billReceivable__c")) {
                n.setSubject("应收审批流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3305850362534575L)));//链接
            }
            if (Objects.equals(entityApiKey, "opportunity")) {
                n.setSubject("业务机会流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3L)));//链接
            }
            if (Objects.equals(entityApiKey, "Intermediary_fee__c")) {
                n.setSubject("居间费用流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3527782912793154L)));//链接
            }
            if (Objects.equals(entityApiKey, "Official_letters__c")) {
                n.setSubject("对外公函流程待办");//标题
                n.setLink(encodeURL(String.format(crmUrl, entityApiKey, dataId, entityApiKey, dataId, 3523778323520141L)));//链接
            }

            CommonHttpClient commonHttpClient = CommonHttpClient.instance();
            CommonData commonData = CommonData.newBuilder()
                    .callType("POST")
                    .header("Authorization", this.basicAuth())
                    .callString(sendTodoUrl)
                    .body(JSON.toJSONString(n))
                    .build();

            commonHttpClient.execute(commonData, responseBody -> {
                logger.info(String.format("调用发送待办接口返回:%s,入参:%s", responseBody, commonData.getBody()));
                return JSONObject.parseObject(responseBody, new TypeReference<NotifyTodoAppResult>() {
                });
            });
        }
    }

    /**
     * 删除待办接口
     *
     * @param docs OA待办列表
     */
    public void deleteTodo(Docs docs) throws XsyHttpException {
        NotifyTodoRemoveContext n = new NotifyTodoRemoveContext();
        n.setAppName(appName);
        n.setModelName(docs.getModelName());
        n.setModelId(docs.getModelId());
        n.setOptType(1);

        CommonHttpClient commonHttpClient = CommonHttpClient.instance();
        CommonData commonData = CommonData.newBuilder()
                .callType("POST")
                .header("Authorization", this.basicAuth())
                .callString(deleteTodoUrl)
                .body(JSON.toJSONString(n))
                .build();

        commonHttpClient.execute(commonData, responseBody -> {
            logger.info(String.format("调用删除待办接口返回:%s,入参:%s", responseBody, commonData.getBody()));
            return JSONObject.parseObject(responseBody, new TypeReference<NotifyTodoAppResult>() {
            });
        });
    }

    public void setTodoDone(Docs docs) throws XsyHttpException {
        NotifyTodoRemoveContext n = new NotifyTodoRemoveContext();
        n.setAppName(appName);
        n.setModelName(docs.getModelName());
        n.setModelId(docs.getModelId());
        n.setOptType(1);

        CommonHttpClient commonHttpClient = CommonHttpClient.instance();
        CommonData commonData = CommonData.newBuilder()
                .callType("POST")
                .header("Authorization", this.basicAuth())
                .callString(setTodoDoneUrl)
                .body(JSON.toJSONString(n))
                .build();

        commonHttpClient.execute(commonData, responseBody -> {
            logger.info(String.format("调用设为已办接口返回:%s,入参:%s", responseBody, commonData.getBody()));
            return JSONObject.parseObject(responseBody, new TypeReference<NotifyTodoAppResult>() {
            });
        });
    }

    /**
     * 获取待办接口
     *
     * @param loginName 个人登录名
     */
    public NotifyMessageContext getTodo(String loginName) throws XsyHttpException {
        JSONObject loginNameObj = new JSONObject().fluentPut("LoginName", loginName);
        JSONArray otherCond = new JSONArray().fluentAdd(new JSONObject().fluentPut("appName", appName));

        NotifyTodoGetContext n = new NotifyTodoGetContext();
        n.setTargets(loginNameObj.toJSONString());//待办所属对象
        n.setType(1);
        n.setOtherCond(otherCond.toJSONString());
        n.setRowSize(100);

        CommonHttpClient commonHttpClient = CommonHttpClient.instance();
        CommonData commonData = CommonData.newBuilder()
                .callType("POST")
                .header("Authorization", this.basicAuth())
                .callString(getTodoUrl)
                .body(JSON.toJSONString(n))
                .build();

        CommonResponse<NotifyMessageContext> commonResponse = commonHttpClient.execute(commonData, responseBody -> {
            logger.info(String.format("入参:%s,调用获取待办接口返回:%s", commonData.getBody(), responseBody));
            JSONObject obj = JSONObject.parseObject(responseBody);
            if (obj.getInteger("returnState") == 2) {
                JSONObject messageObj = JSONObject.parseObject(obj.getString("message"));
                if (messageObj.containsKey("errorPage") && messageObj.getBoolean("errorPage")) {
                    logger.error("查询OA待办失败!返回:" + messageObj.getString("message"));
                    return null;
                }
                return JSONObject.parseObject(obj.getString("message"), new TypeReference<NotifyMessageContext>() {
                });
            } else {
                logger.error("查询OA待办失败!");
                return null;
            }
        });
        return commonResponse.getData();
    }

    /**
     * @param entityApiKey  对象api名称
     * @param dataId        数据ID
     * @param usertaskLogId 当前任务ID
     * @param operate       执行事件 eg:通过后、拒绝后
     */
    public void handleTodo(String entityApiKey, Long dataId, Long usertaskLogId, String operate) throws ApiEntityServiceException, XsyHttpException {
        List<FlowHistory> flowHistories = creekflowHistory(entityApiKey, dataId, false);
        logger.info(String.format("查询到历史:%s", flowHistories.size()));
        Optional<FlowHistory> optionalFlowHistory = flowHistories.stream().filter(f -> Objects.equals(f.getId(), usertaskLogId)).findFirst();
        String loginName = null;
        Long procInstId = null;
        if (optionalFlowHistory.isPresent()) {
            FlowHistory currentFlow = optionalFlowHistory.get();
            procInstId = currentFlow.getProcInstId();
            logger.info(String.format("currentFlow:%s", JSON.toJSONString(currentFlow)));
            Long assigneeId = currentFlow.getAssigneeId();
            User queryUser = new User();
            queryUser.setId(assigneeId);
            User user = XObjectService.instance().get(queryUser, true);
            if (user.getYesNo__c() == 1) loginName = user.getLoginName__c();
        }
        if (loginName == null) return;

        //对象api名称+数据ID+流程实例ID 用“-”拼接成一个唯一标识
        //String modelId = String.format("%s-%s-%s", entityApiKey, dataId, procInstId);
        //数据ID+流程实例ID 用“-”拼接成一个唯一标识
        String modelId = String.format("%s-%s", dataId, procInstId);
        NotifyMessageContext message = getTodo(loginName);
        logger.info("message:" + JSON.toJSONString(message));
        if (message == null || message.getDocs().isEmpty()) return;

        List<Docs> docsList = message.getDocs().stream().filter(m -> Objects.equals(m.getModelId(), modelId)).collect(Collectors.toList());

        if (!docsList.isEmpty())
            for (Docs docs : docsList) {
                //节点同意后事件、节点拒绝后事件
                if (Objects.equals(operate, "agree") || Objects.equals(operate, "reject"))
                    setTodoDone(docs);
                //流程撤回后事件
                if (Objects.equals(operate, "withdraw"))
                    deleteTodo(docs);
            }
    }

    public String milliToLocalDateTimeStr(long milli, String pattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        Instant instant = Instant.ofEpochMilli(milli); //将时间戳转换为Instant对象
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); //将Instant对象转换为LocalDateTime对象

        return dateTimeFormatter.format(localDateTime);
    }

    public String milliToLocalDateStr(long milli, String pattern) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern);
        Instant instant = Instant.ofEpochMilli(milli); //将时间戳转换为Instant对象
        LocalDate localDate = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate();
        LocalDateTime localDateTime = localDate.atStartOfDay();

        return dateTimeFormatter.format(localDateTime);
    }

    private String basicAuth() {
        return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8));
    }

    public String encodeURL(String url) {
        try {
            String tempUrl = URLEncoder.encode(url, CharEncoding.UTF_8);
            return baseUrl + URLEncoder.encode("=" + tempUrl, CharEncoding.UTF_8);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询审批历史
     *
     * @param entityApiKey 对象apiKey
     * @param dataId       数据ID
     * @param stageFlg     是否为阶段审批
     */
    public List<FlowHistory> creekflowHistory(String entityApiKey, long dataId, boolean stageFlg) {
        RkhdHttpData rkhdHttpData = RkhdHttpData.newBuilder()
                .callString(String.format("/rest/data/v2.0/creekflow/history/filter?entityApiKey=%s&dataId=%s&stageFlg=%s", entityApiKey, dataId, stageFlg))
                .callType("GET")
                .build();
        try {
            CommonResponse<List<FlowHistory>> response = RkhdHttpClient.instance().execute(rkhdHttpData, s -> JSONObject.parseObject(s, new TypeReference<CommonResponse<List<FlowHistory>>>() {
            }));
            logger.debug("查询审批历史:" + JSON.toJSONString(response));
            if (response.getCode() != 200) throw new RuntimeException("查询审批历史错误！");

            return response.getData();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        OAService s = new OAService();
        String s1 = s.encodeURL("/bff/neoweb#/entityDetail/opportunity/3354030995216969?objectApiKey=opportunity&recordId=3354030995216969&objectId=3");
        System.out.println(s1);
        s.sendTodo("contract", 3230694220860773L, 3230869218971003L);
    }

}
